package com.partsoft.dits.message;

import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;

import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;

@SuppressWarnings({ "unchecked" })
public class BaseMessageDataItem extends AbstractMessageDataItem implements MessageDataItem, Map<String, String>,
		Cloneable {

	private static final long serialVersionUID = 8754807085539841743L;

	private DataAction dataAction;

	private Map<DataProperty, String> values = Collections.EMPTY_MAP;

	public BaseMessageDataItem(MessageEntity owner) {
		super(owner);
	}

	public DataAction getAction() {
		return dataAction == null ? getOwner().getHeader().getDefaultAction() : dataAction;
	}

	public void setAction(DataAction action) {
		this.dataAction = action;
	}

	protected Map<DataProperty, String> getRawValues() {
		if (values == Collections.EMPTY_MAP)
			values = new LinkedHashMap<DataProperty, String>();
		return values;
	}

	public Map<DataProperty, String> getValues() {
		return Collections.unmodifiableMap(values);
	}

	public void setValues(Map<DataProperty, String> values) {
		if (CollectionUtils.isEmpty(values)) {
			this.values = Collections.EMPTY_MAP;
		} else {
			this.values = new LinkedHashMap<DataProperty, String>(values);
		}
	}

	public boolean hasValues() {
		return !CollectionUtils.isEmpty(values);
	}

	public String putValue(DataProperty column, String value) {
		Assert.notNull(column);
		if (!StringUtils.hasText(value))
			value = "";
		return getRawValues().put(column, value);
	}

	public String putValue(String columnName, String value) {
		return putValue(new DataProperty(columnName), value);
	}

	public String removeValue(DataProperty column) {
		if (CollectionUtils.isEmpty(values))
			return "";
		return values.remove(column);
	}

	public String removeValue(String columnName) {
		return removeValue(new DataProperty(columnName));
	}

	public boolean isEmpty() {
		return values.isEmpty();
	}

	public boolean containsKey(Object obj) {
		return obj == null ? false : values.containsKey(new DataProperty(obj.toString()));
	}

	public boolean containsValue(Object obj) {
		return values.containsValue(obj);
	}

	public String get(Object obj) {
		DataProperty ppo = new DataProperty(obj.toString());
		String result = obj != null ? values.get(ppo) : null;
		if (result == null) {
			if (ObjectUtils.nullSafeEquals(ppo, DataProperty.DATA_ACTION)) {
				result = getAction().name();
			}
		}
		return result;
	}

	public void clear() {
		if (!CollectionUtils.isEmpty(values))
			values = Collections.EMPTY_MAP;
	}

	public Set<String> keySet() {
		Set<String> resultKeySet = new LinkedHashSet<String>();
		if (hasValues()) {
			for (DataProperty cln : values.keySet()) {
				resultKeySet.add(cln.getPropertyName());
			}
		}
		resultKeySet.add(DataProperty.DATA_ACTION.getPropertyName());
		return new HashSet<String>(resultKeySet);
	}

	public Set<java.util.Map.Entry<String, String>> entrySet() {
		Set<java.util.Map.Entry<String, String>> resultEntrySet = new LinkedHashSet<Map.Entry<String, String>>();
		boolean appendActionEntry = true;
		if (hasValues()) {
			for (java.util.Map.Entry<DataProperty, String> clnEntry : values.entrySet()) {
				if (ObjectUtils.nullSafeEquals(clnEntry.getKey(), DataProperty.DATA_ACTION)) {
					appendActionEntry = false;
				}
				ColumnMapEntry entry = new ColumnMapEntry(clnEntry);
				resultEntrySet.add(entry);
			}
		}

		if (appendActionEntry) {
			resultEntrySet.add(new java.util.Map.Entry<String, String>() {
				public String getKey() {
					return DataProperty.DATA_ACTION.getPropertyName();
				}

				public String getValue() {
					return getAction().name();
				}

				public String setValue(String value) {
					return getAction().name();
				}

			});
		}
		return resultEntrySet;
	}

	public String put(String key, String value) {
		return putValue(key, value);
	}

	public void putAll(Map<? extends String, ? extends String> m) {
		if (CollectionUtils.isEmpty(m))
			return;
		if (!hasValues())
			values = new LinkedHashMap<DataProperty, String>();

		for (java.util.Map.Entry<? extends String, ? extends String> entrys : m.entrySet()) {
			values.put(new DataProperty(entrys.getKey()), entrys.getValue());
		}
	}

	public int size() {
		return values.size();
	}

	public String remove(Object obj) {
		if (!hasValues())
			return null;
		return values.remove(obj == null ? null : new DataProperty(obj.toString()));
	}

	public Collection<String> values() {
		return values.values();
	}

	@Override
	public BaseMessageDataItem clone() {
		BaseMessageDataItem bdi = null;
		try {
			bdi = (BaseMessageDataItem) super.clone();
		} catch (Throwable e) {
			throw new IllegalStateException(e);
		}
		if (bdi.hasValues()) {
			bdi.values = new LinkedHashMap<DataProperty, String>(bdi.getValues());
		}
		return bdi;
	}

	@Override
	public String toString() {
		return "row [action=" + getAction() + ", fields=" + getValues() + ", pks={" + Arrays.toString(getParents())
				+ "}, childs==" + Arrays.toString(getChilds()) + "}]";
	}

	private static class ColumnMapEntry implements java.util.Map.Entry<String, String> {

		private final java.util.Map.Entry<DataProperty, String> fEntry;

		public ColumnMapEntry(java.util.Map.Entry<DataProperty, String> fEntry) {
			this.fEntry = fEntry;
		}

		public String getKey() {
			return fEntry.getKey().getPropertyName();
		}

		public String getValue() {
			return fEntry.getValue();
		}

		public String setValue(String value) {
			return fEntry.setValue(value);
		}

		@Override
		public int hashCode() {
			final int prime = 31;
			int result = 1;
			result = prime * result + ((fEntry == null) ? 0 : fEntry.hashCode());
			return result;
		}

		@Override
		public boolean equals(Object obj) {
			if (this == obj)
				return true;
			if (obj == null)
				return false;
			if (getClass() != obj.getClass())
				return false;
			ColumnMapEntry other = (ColumnMapEntry) obj;
			if (fEntry == null) {
				if (other.fEntry != null)
					return false;
			} else if (!fEntry.equals(other.fEntry))
				return false;
			return true;
		}
	}

}
